﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WebCrawler.Crawler.Interfaces;
using WebCrawler.HtmlParser.Interfaces;
using WebCrawler.Graph;
using WebCrawler.Graph.Interfaces;
using WebCrawler.HtmlParser;
using System.Threading;
using WebCrawler.HttpConnection;
using HttpConnection;

namespace WebCrawler.Crawler
{
    public class SecondCrawler : ICrawler
    {
        List<string> _visitedAddresses;
        IBasicGraph _siteMap;
        IHtmlDoc _startPage;
        DocVertex _mainRoot;

        public int MaxDepth { get; set; }
        public string Url 
        {
            get { return _startPage.Url; }
            set { _startPage.Url = value; }
        }

        public SecondCrawler()
        {
            _siteMap = new BasicGraph();
            _visitedAddresses = new List<string>();
        }

        public SecondCrawler(string url)
            : this()
        {
            _startPage = new HtmlDoc(url);
            _mainRoot = new DocVertex(_startPage) { level = 1 };

            MaxDepth = 5;
        }

        public IBasicGraph Graph
        {
            get { return _siteMap; }
        }

        public void BuildSiteMap()
        {
            Queue<DocVertex> documentsWaiting = new Queue<DocVertex>();
            documentsWaiting.Enqueue(new DocVertex(_startPage));

            int depthCounter = 1;

            while (documentsWaiting.Any(p=>p.level < 5))
            {
                DocVertex vertex = documentsWaiting.Dequeue();

                Log("Checking if vertex {0} has been visited before", vertex.Url);
                if (!AlreadyVisited(vertex))
                {
                    Log("Vertex has not been visited before");
                    _siteMap.AddVertex(vertex);
                    _visitedAddresses.Add(vertex.Document.Url);

                    foreach (var link in vertex.Document.GetAllLinks())
                    {
                        Uri uri;
                        if (UrlParser.ValidateUrl(link, out uri))
                        {
                            DocVertex toVertex = new DocVertex(new HtmlDoc(uri.ToString())) { level = depthCounter };
                            documentsWaiting.Enqueue(toVertex);
                            Log("Added {0} to queue", toVertex.Url);
                            vertex.Edges.Add(new Edge(vertex, toVertex));
                            Log("Added edge between: {0} and {1}", vertex.Url, toVertex.Url);
                        }
                    }

                    depthCounter++;
                    Log("Next whileiteration depth: {0}", depthCounter.ToString());
                    Log("");
                }
                else
                {
                    Log("Vertex has been visited before");
                }
            }
        }

        private void Log(string value, params string[] values)
        {
            Console.WriteLine(value, values);
        }

        private bool AlreadyVisited(DocVertex vertex)
        {
            foreach (var address in _visitedAddresses)
            {
                if (address == vertex.Document.Url)
                {
                    return true;
                }
            }

            return false;
        }

        // Unsuported interface

        public int MAXDEPTH
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public System.Collections.IList GraphTraverse()
        {
            throw new NotImplementedException();
        }

        // Temporary
        public void BuildSiteMapTest321()
        {
            throw new NotImplementedException();
        }


        public bool Dubplicate
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }


        public void Traversal()
        {
            throw new NotImplementedException();
        }


        public IVertex RootVertex
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }


        public void Traversal(out Thread t)
        {
            throw new NotImplementedException();
        }
    }
}
